/**
 * 
 */
package com.dalonedrau.jogl.util;

import java.awt.Color;
import java.awt.Point;
import java.nio.FloatBuffer;

import com.dalonedrau.jogl.engine.GlobalConsts;
import com.dalonedrau.jogl.opengl.ui.GuiComponent;
import com.dalonedrau.jogl.opengl.ui.Label;
import com.dalonedrau.jogl.opengl.ui.UIFactory;
import com.dalonedrau.jogl.opengl.ui.border.EmptyBorder;
import com.dalonedrau.vectors.Vector2;

/**
 * @author Donald
 */
public final class UtilityMethods {
	/** the one and only instance of the <code>UtilityMethods</code> class. */
	private static UtilityMethods	instance;
	/**
	 * Gives access to the singleton instance of {@link UtilityMethods}.
	 * @return {@link UtilityMethods}
	 */
	public static UtilityMethods getInstance() {
		if (UtilityMethods.instance == null) {
			UtilityMethods.instance = new UtilityMethods();
		}
		return UtilityMethods.instance;
	}
	/**
	 * Extends an int array by one value.
	 * @param src the source array
	 * @param val the new values being added to the array
	 * @return <code>int</code>[]
	 */
	public int[] extendIntArray(final int[] src, final int... val) {
		int[] dest = new int[src.length + val.length];
		System.arraycopy(src, 0, dest, 0, src.length);
		System.arraycopy(val, 0, dest, src.length, val.length);
		return dest;
	}
	/**
	 * Centers a {@link GuiComponent} in the main application frame.
	 * @param component the {@link GuiComponent} being centered
	 * @return {@link Point}
	 */
	public Point centerComponentInFrame(final GuiComponent component) {
		// the origin in the orthographic projection
		// is 0, 0 at the bottom-left corner
		int x = 0, y = 0;
		x = GlobalConsts.SCREEN_WIDTH - component.getPreferredSize().width;
		x /= 2;
		y = GlobalConsts.SCREEN_HEIGHT - component.getPreferredSize().height;
		y /= 2;
		return new Point(x, y);
	}
	/**
	 * Converts a {@link Color} to a {@link FloatBuffer}.
	 * @param color the {@link Color}
	 * @return {@link FloatBuffer}
	 */
	public FloatBuffer colorToBuffer(final Color color) {
		return FloatBuffer.wrap(color.getRGBComponents(null));
	}
	/**
	 * Checks whether this <code>Rectangle</code> entirely contains the
	 * <code>Rectangle</code> at the specified location {@code (X,Y)} with the
	 * specified dimensions {@code (W,H)}.
	 * @param r1x the specified X coordinate
	 * @param r1y the specified Y coordinate
	 * @param r1w the width of the <code>Rectangle</code>
	 * @param r1h the height of the <code>Rectangle</code>
	 * @return <code>true</code> if the <code>Rectangle</code> specified by
	 *         {@code (X, Y, W, H)} is entirely enclosed inside this
	 *         <code>Rectangle</code>; <code>false</code> otherwise.
	 * @since 1.1
	 */
	public boolean contains(final int r0x, final int r0y, final int r0w,
					final int r0h, final int r1x, final int r1y, int r1w,
					int r1h) {
		int w = r0w;
		int h = r0h;
		if ((w | h | r1w | r1h) < 0) {
			// At least one of the dimensions is negative...
			return false;
		}
		// Note: if any dimension is zero, tests below must return false...
		int x = r0x;
		int y = r0y;
		if (r1x < x || r1y < y) {
			return false;
		}
		w += x;
		r1w += r1x;
		if (r1w <= r1x) {
			// X+W overflowed or W was zero, return false if...
			// either original w or W was zero or
			// x+w did not overflow or
			// the overflowed x+w is smaller than the overflowed X+W
			if (w >= x || r1w > w) {
				return false;
			}
		} else {
			// X+W did not overflow and W was not zero, return false if...
			// original w was zero or
			// x+w did not overflow and x+w is smaller than X+W
			if (w >= x && r1w > w) {
				return false;
			}
		}
		h += y;
		r1h += r1y;
		if (r1h <= r1y) {
			if (h >= y || r1h > h) {
				return false;
			}
		} else {
			if (h >= y && r1h > h) {
				return false;
			}
		}
		return true;
	}
	/**
	 * Converts an integer value to a {@link Point}.
	 * @param val the integer
	 * @return {@link Point}
	 */
	public Point convertIntToPoint(final int val) {
		return new Point(val >>> GlobalConsts.INT_016, val & 0xffff);
	}
	/**
	 * Converts a milliseconds figure to nanoseconds.
	 * @param ms the milliseconds
	 * @return long
	 */
	public long convertMilliToNano(final long ms) {
		long time = ms;
		// convert to milli
		final int base = 10, power = 6;
		time *= Math.pow(base, power);
		return time;
	}
	/**
	 * Converts a nanoseconds figure to milliseconds.
	 * @param ns the nanoseconds
	 * @return long
	 */
	public long convertNanoToMilli(final long ns) {
		long time = ns;
		// convert to milli
		final int base = 10, power = -6;
		time *= Math.pow(base, power);
		return time;
	}
	/**
	 * Converts a coordinate to an integer.
	 * @param x the x-coordinate value
	 * @param y the y-coordinate value
	 * @return <code>int</code>
	 */
	public int convertPointToInt(final float x, final float y) {
		int val = (int) x << GlobalConsts.INT_016;
		val += (int) y;
		return val;
	}
	/**
	 * Converts a coordinate to an integer.
	 * @param pt the coordinates
	 * @return <code>int</code>
	 */
	public int convertPointToInt(final Point pt) {
		return this.convertPointToInt(pt.x, pt.y);
	}
	/**
	 * Converts a coordinate to an integer.
	 * @param v the coordinates
	 * @return <code>int</code>
	 */
	public int convertPointToInt(final Vector2 v) {
		return this.convertPointToInt((float) v.getX(), (float) v.getY());
	}
	/**
	 * Gets a blank spacer label intended to create space between elements.
	 * @param width the spacer's width
	 * @param height the spacer's height
	 * @return {@link Label}
	 */
	public Label getSpacer(final int width, final int height) {
		Label l = UIFactory.getInstance().getStandardH1Label("");
		l.setBorder(new EmptyBorder(height, width, 0, 0));
		return l;
	}
	/**
	 * Gets the suffix to a file's name, splitting it at the '.'.
	 * @param word the word
	 * @return {@link String}
	 */
	public String getSuffix(final String word) {
		String[] split = word.split("\\.");
		return split[split.length - 1];
	}
	/**
	 * Determines whether or not this <code>Rectangle</code> and the specified
	 * <code>Rectangle</code> intersect. Two rectangles intersect if their
	 * intersection is nonempty.
	 * @param r0x the 1st rectangle's x-coordinate
	 * @param r0y the 1st rectangle's y-coordinate
	 * @param r0w the 1st rectangle's width
	 * @param r0h the 1st rectangle's height
	 * @param r1x the 1st rectangle's x-coordinate
	 * @param r1y the 1st rectangle's y-coordinate
	 * @param r1w the 1st rectangle's width
	 * @param r1h the 1st rectangle's height
	 * @return <code>true</code> if the specified <code>Rectangle</code> and
	 *         this <code>Rectangle</code> intersect; <code>false</code>
	 *         otherwise.
	 */
	public boolean intersects(final int... pts) {
		int tw = pts[2];
		int th = pts[3];
		int rw = pts[6];
		int rh = pts[7];
		if (rw <= 0 || rh <= 0 || tw <= 0 || th <= 0) {
			return false;
		}
		int tx = pts[0];
		int ty = pts[1];
		int rx = pts[4];
		int ry = pts[5];
		rw += rx;
		rh += ry;
		tw += tx;
		th += ty;
		// overflow || intersect
		return (rw < rx || rw > tx)
				&& (rh < ry || rh > ty)
				&& (tw < tx || tw > rx)
				&& (th < ty || th > ry);
	}
}
